#include "UDPNet_Test.h"

#include "misc/UDPNet.h"
#include "misc/Event.h"
#include <Poco/Net/NetworkInterface.h>
#include <Poco/Net/NetException.h>
#include <Poco/Net/SocketAddress.h>
#include <Poco/Exception.h>
#include <iostream>
/*********************************************************/
std::string UDPNetTest::name()
{
	return("UDPNet");
}
/*********************************************************/
bool UDPNetTest::runUnitTests()
{
	bool result = true;

	if ( !cleanStartStopTest() ) result = false;
	if ( !dataSendSelfTest() ) result = false;
	if ( !dataSendRemoteTest() ) result = false;
	if ( !eventFlagResponseTest() ) result = false;
	if ( !doubleStartTest() ) result = false;
	if ( !doubleStopTest() ) result = false;
	if ( !idleStopTest() ) result = false;
	if ( !addressReturnTest() ) result = false;
	if ( !sendToTest() ) result = false;

	return( result ) ;
}
/*********************************************************/
bool UDPNetTest::sendToTest()
{
	std::cout << "Running: sendToTest()";
	std::cout.flush();
	try {
		std::string	msg = "Hello World, this is the sendToTest sending test bytes";
		UDPNet	 	net("localhost", 4000);
		UDPNet		net2("localhost", 4001);
		net.setResponseAddress("localhost", 5001);		// Set a different response address here 

		// Start both udp classes
		net.start("TRAFFIC");
		net2.start("TRAFFIC");

		net.sendTo(msg, "localhost:4001");
		while( !net2.available() );

		std::string result = net2.pop();

		if ( result != msg ) {
			net.stop();
			net2.stop();
			std::cout << "... failed!" << std::endl;
			return( false );	// test failed!
		}

		net.stop();
		net2.stop();
		std::cout << "... success!" << std::endl;
		return( true );			// Succes!
	} catch(...) {
		std::cout << "... failed! Caught unexpected exception" << std::endl;
		return false;
	}
	std::cout << "... failed!" << std::endl;
	return false;
}
/*********************************************************/
bool UDPNetTest::idleStopTest()
{
	std::cout << "Running: idleStopTest()";
	std::cout.flush();
	try {
		UDPNet	udp("127.0.0.1", 4001);
		udp.stop();
	} catch(Poco::Net::NetException& e) {
		std::cout << "... failed with exception: " << e.displayText() << std::endl;
		return( false );
	} catch(Poco::InvalidArgumentException& e) {
		std::cout << "... failed with exception: " << e.displayText() << std::endl;
		return( false );
	} catch(...) {
		std::cout << "... failed with unknown exception" << std::endl;
		return( false );
	}
	std::cout << "... success!" << std::endl;
	return( true );
}
/*********************************************************/
bool UDPNetTest::doubleStopTest()
{
	std::cout << "Running: doubleStopTest()";
	std::cout.flush();
	try {
		UDPNet	udp("127.0.0.1", 4001);
		udp.start("JUNK");
		udp.stop();
		udp.stop();
	} catch(Poco::Net::NetException& e) {
		std::cout << "... failed with exception: " << e.displayText() << std::endl;
		return( false );
	} catch(Poco::InvalidArgumentException& e) {
		std::cout << "... failed with exception: " << e.displayText() << std::endl;
		return( false );
	} catch(...) {
		std::cout << "... failed with unknown exception" << std::endl;
		return( false );
	}
	std::cout << "... success!" << std::endl;
	return( true );
}
/*********************************************************/
bool UDPNetTest::doubleStartTest()
{
	std::cout << "Running: doubleStartTest()";
	std::cout.flush();
	try {
		UDPNet	udp("127.0.0.1", 4001);
		udp.start("JUNK");
		udp.start("JUNK");
		udp.stop();
	} catch(Poco::Net::NetException& e) {
		std::cout << "... failed with exception: " << e.displayText() << std::endl;
		return( false );
	} catch(Poco::InvalidArgumentException& e) {
		std::cout << "... failed with exception: " << e.displayText() << std::endl;
		return( false );
	} catch(...) {
		std::cout << "... failed with unknown exception" << std::endl;
		return( false );
	}
	std::cout << "... success!" << std::endl;
	return( true );
}
/*********************************************************/
bool UDPNetTest::cleanStartStopTest()
{
	std::cout << "Running: cleanStartStopTest()";
	std::cout.flush();
	try {
		UDPNet	udp("127.0.0.1", 4001);
		udp.start("TRAFFIC");
		udp.stop();
	} catch(Poco::Net::NetException& e) {
		std::cout << "... failed with exception: " << e.displayText() << std::endl;
		return( false );
	} catch(Poco::InvalidArgumentException& e) {
		std::cout << "... failed with exception: " << e.displayText() << std::endl;
		return( false );
	} catch(...) {
		std::cout << "... failed with unknown exception" << std::endl;
		return( false );
	}
	std::cout << "... success!" << std::endl;
	return( true );
}
/*********************************************************/
bool UDPNetTest::eventFlagResponseTest()
{
	try {
		std::cout << "Running: eventFlagResponseTest()";
		std::string eventStr = "TRAFFIC";
		Event*	event = Event::Instance(eventStr);
		std::string	msg = "eventFlagResponse Test Packet";
		UDPNet	 	net("localhost", 4000);
		net.setResponseAddress("localhost", 4000);
		net.start(eventStr);

		net.send(msg);
		event->wait();		// Wait for the event to be flagged by UDPNet
		std::string result = net.pop();	

		if ( result != msg ) {
			net.stop();
			std::cout << "... Failed! Data not identical" << std::endl;
			return( false );	// test failed!
		}

		net.stop();
		std::cout << "... success!" << std::endl;
		return( true );			// Succes!
	} catch(Poco::Exception& e) {
		std::cout << "... Exception caught: " << e.displayText() << "... Failed!" << std::endl;
		return( false);
	} catch(std::exception& e){
		std::cout << "... Exception caught: " << e.what() << "... Failed!" << std::endl;
		return( false);
	} catch(...) {
		std::cout << "... Unknown exception caught, No response when waiting for event... Failed!" << std::endl;
		return( false );
	}
}
/*********************************************************/
bool UDPNetTest::addressReturnTest()
{
	try {
		std::cout << "Running: addressReturnTest()";
		std::string	msg = "Hello World, this is the addressReturnTest sending test bytes";
		UDPNet	 	net("127.0.0.1", 4000);
		UDPNet		net2("127.0.0.1", 4001);
		net.setResponseAddress("localhost", 4001);

		net.start("TRAFFIC");
		net2.start("TRAFFIC");

		net.send(msg);
		while( !net2.available() );

		Poco::Net::SocketAddress	addr;
		std::string result = net2.pop(addr);

		if ( (addr.host() != Poco::Net::IPAddress("127.0.0.1")) || (addr.port() != 4000)) {
			net.stop();
			net2.stop();
			std::cout << "...failed! Sender address incorrect!" << std::endl;
			return( false );
		}

		if ( result != msg ) {
			net.stop();
			net2.stop();
			std::cout << "... failed! Data sent different from data received!" << std::endl;
			return( false );	// test failed!
		}

		net.stop();
		net2.stop();
		std::cout << "... success!" << std::endl;
		return( true );			// Succes!
	} catch( std::exception& e ) {
		std::cout << "... failed! with exception: " << e.what() << std::endl;
		return( false );
	}
}
/*********************************************************/
bool UDPNetTest::dataSendRemoteTest()
{
	std::cout << "Running: dataSendRemoteTest()";
	std::string	msg = "Hello World, this is the dataSendRemoteTest sending test bytes";
	UDPNet	 	net("localhost", 4000);
	UDPNet		net2("localhost", 4001);
	net.setResponseAddress("localhost", 4001);

	net.start("TRAFFIC");
	net2.start("TRAFFIC");

	net.send(msg);
	while( !net2.available() );

	std::string result = net2.pop();

	if ( result != msg ) {
		net.stop();
		net2.stop();
		std::cout << "... failed!" << std::endl;
		return( false );	// test failed!
	}

	net.stop();
	net2.stop();
	std::cout << "... success!" << std::endl;
	return( true );			// Succes!
}
/*********************************************************/
bool UDPNetTest::dataSendSelfTest()
{
	std::cout << "Running: dataSendSelfTest()";
	std::string	msg = "Hello World, this is the dataSendSelfTest sending test bytes";
	UDPNet	 	net("localhost", 4000);
	net.setResponseAddress("localhost", 4000);
	net.start("TRAFFIC");

	net.send(msg);
	while( !net.available() );

	std::string result = net.pop();

	if ( result != msg ) {
		net.stop();
		std::cout << "... failed!" << std::endl;
		return( false );	// test failed!
	}

	net.stop();
	std::cout << "... success!" << std::endl;
	return( true );			// Succes!
}
